/**
 * @file LoopFIFO.c
 * @brief
 * @author xuesong (wenshaoliu@foxmail.com)
 * @version 1.0
 * @date 2024-08-20
 *
 * @copyright Copyright (c) 2024  by xuesong
 *
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-08-20     <td>1.0     <td>xuesong   <td>内容
 * </table>
 */
#include <stdlib.h>
#include <stdio.h>
// #include "cs_log.h"
#include "LoopFIFO.h"
//--------------------------------------------------
#define pvPortMalloc malloc
#define vPortFree free
#define LOG(...) printf(__VA_ARGS__)
//--------------------------------------------------
/**
 * @brief
 * @param  pxFifo           fifo object
 * @param  pcBuff           buffer
 * @param  usSize           buffer size
 * @return FifoStatusDef
 */
FifoStatusDef xFifoBufferStaticCreate(fifo_data_t *pxFifo, uint8_t *pcBuff, uint16_t usSize)
{
    /* Setting space is too small. */
    if (usSize < 16)
    {
        return Fifo_SPACE_NOT_ENOUGH;
    }

    /* Buffer must 4byte alignment. */
    if ((((uint32_t)(pcBuff)) % 4) || (usSize % 4))
    {
        LOG("fifo buff must 4byte alignment.\r\n");
        return Fifo_NOT_ALIGNMENT;
    }

    /* Create queue. */
    if (pxFifo != NULL)
    {
        pxFifo->pcBuffer = pcBuff;
        pxFifo->bufferSize = usSize;

        pxFifo->rdIndex = 0;
        pxFifo->wrIndex = 0;
        pxFifo->Flag.Byte = 0;
        pxFifo->Flag.Bits.Init = 1;

        return Fifo_NO_ERR;
    }
    else
    {
        return Fifo_BUFFER_IS_INVALID;
    }
}

/**
 * @brief
 * @param  pxFifo           fifo object
 * @param  usSize           fifo buffer size
 * @return FifoStatusDef
 */
FifoStatusDef xFifoBufferCreate(fifo_data_t *pxFifo, uint16_t usSize)
{
    FifoStatusDef retValue;
    uint8_t *pcMem;
    pcMem = (uint8_t *)pvPortMalloc(usSize);
    retValue = xFifoBufferStaticCreate(pxFifo, pcMem, usSize);
    if (retValue != Fifo_NO_ERR)
    {
        vPortFree(pcMem);
    }
    return retValue;
}

/**
 * @brief
 * @param  pxFifo           fifo object
 * @return FifoStatusDef
 */
FifoStatusDef xFifoBufferDestroy(fifo_data_t *pxFifo)
{
    if (pxFifo != NULL)
    {
        if (pxFifo->bufferSize != 0)
        {

            if (pxFifo->pcBuffer != NULL)
            {
                vPortFree(pxFifo->pcBuffer);
                LOG("stream buffer clear.\r\n");
            }

            /* Clear buffer. */
            memset(pxFifo, 0, sizeof(fifo_data_t));

            /* buffer to empty */
            pxFifo->pcBuffer = NULL;
        }

        return Fifo_NO_ERR;
    }
    else
    {
        return Fifo_BUFFER_IS_INVALID;
    }
}

/**
 * @brief
 * @param  pxFifo           fifo buffer object
 * @return uint16_t         buffer occupy bytes
 */
uint16_t getXFifoBufferOccupy(fifo_data_t *pxFifo)
{
    uint16_t retCnt = 0;
    if (pxFifo->pcBuffer != NULL)
    {
        if (pxFifo->wrIndex == pxFifo->rdIndex)
        {
            if (pxFifo->Flag.Bits.Full)
            {
                retCnt = pxFifo->bufferSize;
            }
        }
        else if ((pxFifo->wrIndex - pxFifo->rdIndex) > 0)
            retCnt = pxFifo->wrIndex - pxFifo->rdIndex;
        else
        {
            retCnt = pxFifo->bufferSize - pxFifo->rdIndex;
            retCnt += pxFifo->wrIndex;
            retCnt++;
        }
    }
    return retCnt;
}
/**
 * @brief
 * @param  pxFifo           fifo buffer object
 * @return uint16_t         buffer free bytes
 */
uint16_t getXFifoBufferFree(fifo_data_t *pxFifo)
{
    uint16_t retCnt = 0;
    if (pxFifo->pcBuffer != NULL)
    {
        retCnt = pxFifo->bufferSize - getXFifoBufferOccupy(pxFifo);
    }
    return retCnt;
}
/**
 * @brief
 * @param  pxFifo           fifo buffer object
 * @param  pcBuff           data point
 * @param  usSize           data len
 * @return FifoStatusDef
 */
FifoStatusDef xFifoBufferWrite(fifo_data_t *pxFifo, uint8_t *pcBuff, uint16_t usSize)
{
    if (pxFifo->pcBuffer == NULL)
    {
        /* error space is not enough. */
        LOG("fifo buffer not init\r\n");
        return Fifo_BUFFER_IS_INVALID;
    }
    if (getXFifoBufferFree(pxFifo) < usSize)
    {
        /* error space is not enough. */
        LOG("space is not enough.\r\n");
        return Fifo_SPACE_NOT_ENOUGH;
    }
    for (uint16_t i = 0; i < usSize; i++)
    {
        if (pxFifo->wrIndex >= pxFifo->bufferSize)
            pxFifo->wrIndex = 0;
        pxFifo->pcBuffer[pxFifo->wrIndex++] = pcBuff[i];
    }
    if (pxFifo->wrIndex >= pxFifo->bufferSize)
        pxFifo->wrIndex = 0;
    if (pxFifo->rdIndex == pxFifo->wrIndex)
        pxFifo->Flag.Bits.Full = 1;
    return Fifo_NO_ERR;
}

FifoStatusDef xFifoBufferPeek(fifo_data_t *pxFifo, uint8_t *pcBuff, uint16_t offset, uint16_t usSize)
{
    int32_t bufferDataLen;
    if (pxFifo->pcBuffer == NULL)
    {
        /* error space is not enough. */
        LOG("fifo buffer not init\r\n");
        return Fifo_BUFFER_IS_INVALID;
    }
    bufferDataLen = getXFifoBufferOccupy(pxFifo);
    bufferDataLen -= offset;
    if (bufferDataLen <= 0)
    {
        /* error space is not enough. */
        LOG("fifo data not enough\r\n");
        return Fifo_SPACE_NOT_ENOUGH;
    }
    else if (bufferDataLen < (usSize))
    {
        /* error data is not enough. */
        LOG("buffer data = %d\r\n", bufferDataLen);
        return Fifo_SPACE_NOT_ENOUGH;
    }
    bufferDataLen = pxFifo->rdIndex + offset;
    for (uint16_t i = 0; i < (usSize); i++)
    {
        if (bufferDataLen >= pxFifo->bufferSize)
            bufferDataLen = 0;
        pcBuff[i] = pxFifo->pcBuffer[bufferDataLen++];
    }
    return Fifo_NO_ERR;
}
FifoStatusDef xFifoBufferRead2X(fifo_data_t *pxFifo, uint16_t usSize)
{
    if (pxFifo->pcBuffer == NULL)
    {
        /* error space is not enough. */
        LOG("fifo buffer not init\r\n");
        return Fifo_BUFFER_IS_INVALID;
    }
    if (!usSize)
    {
        LOG("read index not move\r\n");
        return Fifo_NO_ERR;
    }
    pxFifo->Flag.Bits.Full = 0;
    if (usSize > pxFifo->bufferSize)
    {
        pxFifo->rdIndex = 0;
        pxFifo->wrIndex = 0;
    }
    uint16_t bufferDataLen = getXFifoBufferOccupy(pxFifo);
    if (bufferDataLen < usSize)
        usSize = bufferDataLen;
    pxFifo->rdIndex += usSize;
    if (pxFifo->rdIndex >= pxFifo->bufferSize)
    {
        pxFifo->rdIndex -= pxFifo->bufferSize;
    }
    return Fifo_NO_ERR;
}
